package csv

import (
	"context"
	"encoding/csv"
	"io"
	"strconv"
	"time"

	"gitee.com/bjf-fhe/apicat/entry"
	"gitee.com/bjf-fhe/apicat/errors"
	"gitee.com/bjf-fhe/apicat/utils"
)

type Reader struct {
	titles   map[string]int
	reader   *csv.Reader
	notifier chan bool
	ctx      context.Context
}

func (r *Reader) NewEntry(rec []string) *entry.LogEntry {
	entry := new(entry.LogEntry)
	if rmi, ok := r.titles["request_method"]; ok && rmi < len(rec) {
		entry.Method = rec[rmi]
	}
	if rmi, ok := r.titles["request_uri"]; ok && rmi < len(rec) {
		entry.Url = rec[rmi]
	}
	if rmi, ok := r.titles["server_protocol"]; ok && rmi < len(rec) {
		entry.Protocol = rec[rmi]
	}
	if rmi, ok := r.titles["status"]; ok && rmi < len(rec) {
		entry.StatusCode, _ = strconv.Atoi(rec[rmi])
	}
	if cip, ok := r.titles["client_ip"]; ok && cip < len(rec) {
		entry.Client = rec[cip]
	}
	if tm, ok := r.titles["time"]; ok && tm < len(rec) {
		entry.Created, _ = time.Parse(time.RFC3339, rec[tm])
	}
	return entry
}

func (r *Reader) Records() chan *entry.LogEntryResponse {
	var c = make(chan *entry.LogEntryResponse)
	var err error
	var hasDone bool
	go func() {
		<-r.ctx.Done()
		hasDone = true
	}()
	go func() {
		for err == nil && !hasDone {
			var rec []string
			rec, err = r.reader.Read()
			if err == nil {
				c <- &entry.LogEntryResponse{
					Entry: r.NewEntry(rec),
				}
			} else if err == io.EOF {
				if r.notifier != nil {
					<-r.notifier
				} else {
					c <- &entry.LogEntryResponse{
						Error: &entry.LogError{
							Error: err,
						},
					}
				}
			} else {
				c <- &entry.LogEntryResponse{
					Error: &entry.LogError{
						Error: err,
					},
				}
			}
		}
	}()

	return c
}

func NewReader(logFile io.Reader, ctx context.Context, tailMode bool) (*Reader, error) {
	reader := csv.NewReader(logFile)
	//先读取一行，获取标题
	titles, err := reader.Read()
	if err == nil {
		tMap := make(map[string]int)
		for k, t := range titles {
			tMap[t] = k
		}
		var r = &Reader{reader: reader, titles: tMap, ctx: ctx}
		if n, ok := logFile.(utils.BufCanBeNotified); ok {
			r.notifier = make(chan bool, 1)
			n.Subscribe(r.notifier)
		}

		return r, nil
	} else if seeker, ok := logFile.(io.Seeker); ok {
		//如果读取标题失败，尝试从头读取一部分内容，作为错误信息
		seeker.Seek(0, 0)
		var head = make([]byte, 512)
		readed, _ := logFile.Read(head)
		err = errors.New(errors.SourceReadContentError, err.Error()+" in read "+string(head[:readed]))
	}
	return nil, err
}
